/****************************************************************************
 * Copyright (C) 2009-2010 SciTouch LLC
 * 
 * This file is part of Indigo toolkit.
 * 
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 3 as published by the Free Software
 * Foundation and appearing in the file LICENSE.GPL included in the
 * packaging of this file.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ***************************************************************************/

#ifndef __molecule_substructure_matcher__
#define __molecule_substructure_matcher__

#include "base_cpp/auto_ptr.h"
#include "graph/embedding_enumerator.h"

#include "molecule/molecule.h"

class Molecule;
class GraphHighlighting;
class AromaticityMatcher;
struct Vec3f;
class GraphVertexEquivalence;
class MoleculeAtomNeighbourhoodCounters;
class MoleculePiSystemsMatcher;

class MoleculeSubstructureMatcher
{
public:
   enum
   {
      AFFINE = 1,
      CONFORMATION = 2
   };

   MoleculeSubstructureMatcher (Molecule &target);

   void setQuery (const Molecule &query);

   // Set vertex neibourhood counters for effective matching
   void setNeiCounters (const MoleculeAtomNeighbourhoodCounters *query_counters, 
                        const MoleculeAtomNeighbourhoodCounters *target_counters);

   bool use_aromaticity_matcher;
   bool use_pi_systems_matcher;
   GraphVertexEquivalence *vertex_equivalence_handler;

   int   match_3d;       // 0 or AFFINE or CONFORMATION
   float rms_threshold;  // for AFFINE and CONFORMATION

   GraphHighlighting *highlighting;

   void ignoreQueryAtom (int idx);
   void ignoreTargetAtom (int idx);
   bool fix (int query_atom_idx, int target_atom_idx);

   // for finding the first embedding
   bool find ();
   const int * getQueryMapping ();
   const int * getTargetMapping ();

   // for finding all embeddings
   bool find_all_embeddings; // false by default
   void (*cb_embedding) (const Graph &sub, const Graph &super, const int *core1, const int *core2, void *context);
   void  *cb_embedding_context;

   static bool needCoords (int match_3d, const Molecule &query);

   enum
   {
      MATCH_ATOM_CHARGE = 0x01,
      MATCH_ATOM_VALENCE = 0x02
   };

   static bool matchAtoms (const Graph &subgraph, const Graph &supergraph,
      const int *core_sub, int sub_idx, int super_idx, dword flags = 0xFFFFFFFFUL);

   enum
   {
      MATCH_BOND_TYPE = 0x01
   };
   
   static bool matchBonds (const Graph &subgraph, const Graph &supergraph,
      int sub_idx, int super_idx, AromaticityMatcher *am, dword flags = 0xFFFFFFFFUL);

   static void addAtom (const Graph &subgraph, const Graph &supergraph,
      int sub_idx, int super_idx, AromaticityMatcher *am);

   static void removeAtom (const Graph &subgraph, int sub_idx, AromaticityMatcher *am);

   static void addBond (const Graph &subgraph, const Graph &supergraph,
      int sub_idx, int super_idx, AromaticityMatcher *am);

   static void markIgnoredHydrogens (const Molecule &mol, int *arr, int value_keep, int value_ignore);

   static void getAtomPos (const Graph &graph, int vertex_idx, Vec3f &pos);

   static void calcQueryHCounters (const Molecule &query, Array<int> &counters, const int *mapping);
   static void calcTargetHCounters (const Molecule &target, Array<int> &counters);

   DEF_ERROR("molecule substructure matcher");

protected:
   
   struct MarkushContext
   {
      explicit MarkushContext (const Molecule &query_, const Molecule &target_);

      TL_CP_DECL(Molecule, query);
      TL_CP_DECL(Array<int>, query_marking);
      TL_CP_DECL(Array<int>, sites);
      int depth;
   };

   static bool _matchAtoms (const Graph &subgraph, const Graph &supergraph,
                            const int *core_sub, int sub_idx, int super_idx, void *userdata);

   static bool _matchBonds (const Graph &subgraph, const Graph &supergraph,
                            int sub_idx, int super_idx, void *userdata);

   static void _addAtom (const Graph &subgraph, const Graph &supergraph,
                         int sub_idx, int super_idx, void *userdata);

   static void _removeAtom (const Graph &subgraph, int sub_idx, void *userdata);

   static void _addBond (const Graph &subgraph, const Graph &supergraph,
                         int sub_idx, int super_idx, void *userdata);

   static int _embedding (const Graph &subgraph, const Graph &supergraph,
                          int *core_sub, int *core_super, void *userdata);

   int _embedding_common (int *core_sub, int *core_super);
   int _embedding_markush (int *core_sub, int *core_super);

   static bool _canUseEquivalenceHeuristic (const Molecule &query);
   static bool _isSingleBond (const Graph &graph, int edge_idx);

   static bool _shouldUnfoldTargetHydrogens (const Molecule &query);

   static int _countSubstituents (const Molecule &mol, int idx);
   
   bool _checkRGroupConditions ();
   bool _attachRGroupAndContinue (int *core1, int *core2,
      const Molecule *fragment, bool two_attachment_points,
      int att_idx1, int att_idx2, int rgroup_idx);

   Molecule &_target;
   const Molecule *_query;

   const MoleculeAtomNeighbourhoodCounters 
      *_query_nei_counters, *_target_nei_counters;

   Obj<EmbeddingEnumerator> _ee;
   AromaticityMatcher  *_am;
   MoleculePiSystemsMatcher *_pi_systems_matcher;

   AutoPtr<MarkushContext> _markush;

   bool _did_h_unfold; // implicit target hydrogens unfolded

   TL_CP_DECL(Array<int>, _3d_constrained_atoms);
   TL_CP_DECL(Array<int>, _unfolded_target_h);
   TL_CP_DECL(Array<int>, _query_h_counters);
   TL_CP_DECL(Array<int>, _target_h_counters);

};

#endif
